package fr.cephb.operonj.tools.cmdline;


import java.io.InputStream;
import java.io.PrintStream;


import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.XMLEvent;

import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.Transaction;

import fr.cephb.operonj.core.bio.Build;
import fr.cephb.operonj.core.db.ReplacePolicy;
import fr.cephb.operonj.core.db.database.BuildDB;

public class BuildLoad extends AbstractApplication
	{
	private BuildDB db=null;
	private ReplacePolicy policy=ReplacePolicy.ERROR_IF_EXISTS;
	
	private BuildLoad()
		{
		super.envCfg.setAllowCreate(true);
		super.envCfg.setReadOnly(false);
		super.envCfg.setTransactional(true);
		}
	
	@Override
	public void close()
		{
		if(db!=null)
			{
			db.safeClose();
			db=null;
			}
		super.close();
		}
	
	@Override
	protected void usage(PrintStream out)
		{
		super.usage(out);
		out.println("Options:");
		out.println(" -policy <value="+policy+"> one of :");
		for(ReplacePolicy p:ReplacePolicy.values())
			{
			out.println("  \t"+p);
			}
		}
	
	@Override
	protected int processArg(String[] args, int optind)
		{
		int n= super.processArg(args, optind);
		if(n!=-1) return n;
		if(args[optind].equals("-policy"))
			{
			this.policy=ReplacePolicy.valueOf(args[++optind]);
			return optind;
			}
		return -1;
		}
	
	private void insert(Transaction txn,XMLEventReader in,Unmarshaller unmarshaller)
		throws XMLStreamException,DatabaseException,JAXBException
		{
		DatabaseEntry key= new DatabaseEntry();
		DatabaseEntry data= new DatabaseEntry();
		XMLEvent evt;
		while(in.hasNext())
			{
			evt=in.peek();
			
			
			if(!evt.isStartElement() ||
				!evt.asStartElement().getName().getLocalPart().equals("build"))
				{
				evt=in.nextEvent();
				continue;
				}
			
			Build b=(Build)unmarshaller.unmarshal(in);
			db.getKeyBinding().objectToEntry(b.getId(), key);
			if(db.getDatabase().get(txn, key, data, LockMode.DEFAULT)==OperationStatus.SUCCESS)
				{
				if(policy==ReplacePolicy.ERROR_IF_EXISTS)
					{
					LOG.warning("build.exist");
					throw new RuntimeException("key already exist: build.id="+b.getId()) ;
					}
				else if(policy==ReplacePolicy.IGNORE_IF_EXISTS)
					{
					LOG.info("ignore");
					continue;
					}
				}
			db.getValueBinding().objectToEntry(b, data);
			if(db.getDatabase().put(txn, key, data)!=OperationStatus.SUCCESS)
				{
				throw new RuntimeException("Failed to insert build.id="+b.getId()) ;
				}
			}
		}
		
	private void load(InputStream input) throws Exception
		{		
		JAXBContext context = JAXBContext.newInstance(Build.class);
		Unmarshaller unmarshaller=context.createUnmarshaller();
		unmarshaller.setSchema(getSchema());
		XMLEventReader r=XMLInputFactory.newInstance().createXMLEventReader(input);
		XMLEvent evt;
		
		Transaction txn= getEnvironment().beginTransaction(null, null);
		if(db==null)
			{
			DatabaseConfig cfgConfig= new DatabaseConfig();
			cfgConfig.setAllowCreate(true);
			cfgConfig.setReadOnly(false);
			cfgConfig.setTransactional(true);
			this.db=new BuildDB(getEnvironment(), txn, null, cfgConfig);
			}
		try
			{
             while(r.hasNext())
             	{
             	evt=r.nextEvent();
             	if(!evt.isStartElement()) continue;
             		
         		if(!evt.asStartElement().getName().getLocalPart().equals("builds"))
         			{
         			throw new XMLStreamException("expected <root> as build");
         			}
         		insert(txn,r,unmarshaller);
             	break;	
             	}
             r.close();	
             LOG.info("commit");
			txn.commit();
			}
		catch (Exception e)
			{
			LOG.warning("transaction aborted");
			txn.abort();
			throw e;
			}
		txn=null;
		
		}
	
	private int run(String args[]) throws Exception
		{
		int optind= processArgs(args);
		open();
		if(optind==args.length)
			{
			LOG.info("Reading stdin");
			load(System.in);
			}
		else
			{
			while(optind<args.length)
				{
				String fname=args[optind++];
				LOG.info("Reading "+fname);
				InputStream in= openStream(fname);
				load(in);
				in.close();
				}
			}
		close();
		return 0;
		}
	
	
	public static void main(String[] args)
		{
		try
			{
			BuildLoad app= new BuildLoad();
			app.run(args);
			}
		catch (Exception err)
			{
			err.printStackTrace();
			}
		}
	}
